// Copyright 2017 Baidu Inc. All Rights Reserved.
// Author: Su Hao (suhao@baidu.com)
//
// Description: Provide the API for external applications.

#include "us_ticker_api.h"
#include "baidu_ca_internal.h"
#include "baidu_ca_mbedtrace.h"
#include "baidu_ca_coap.h"
#include "baidu_ca_conf.h"
#include "baidu_ca_mutex.h"
#include "baidu_json.h"
#include "baidu_ca_timestamp.h"
#include "duer_log.h"

#define BCA_CONF_KEY_UUID       "uuid"
#define BCA_CONF_KEY_TOKEN      "token"
#define BCA_CONF_KEY_HOST       "serverAddr"
#define BCA_CONF_KEY_PORT_REG   "lwm2mPort"
#define BCA_CONF_KEY_PORT_RPT   "coapPort"
#define BCA_CONF_KEY_CERT       "rsaCaCrt"

#define BCA_COAP_LIFETIME       "3600"

#define BCA_SR_REG_PATH         "reg_for_control"
#define BCA_REPORT_PATH         "v1/device/data"

#define BCA_RPT_QUERY_UUID      "deviceUuid="
#define BCA_RPT_QUERY_TOKEN     "token="

#define SR_TAG_UUID             "deviceUUID"
#define SR_TAG_TOKEN            "token"

#if defined(BCA_UDP_REPORTER)
#define SR_HOST                 "117.185.17.43"
#define SR_PORT                 8899
#endif

#define BCA_READ_TIMEOUT        (0)

#define BCA_RES_INC_COUNT       (5)

typedef enum _baidu_ca_start_state_enum {
    BCA_ST_STOPED,
    BCA_ST_UNREGISTERED,
    BCA_ST_REGISTER_CONNECTED,
    BCA_ST_REGISTERED,
    BCA_ST_SR_CONNECTING,
    BCA_ST_SR_DATA_SENDING,
    BCA_ST_SR_DATA_RECEIVING,
    BCA_ST_SR_INITIALIZED,
    BCA_ST_RPT_CONNECTED,
    BCA_ST_RPT_INITIALIZED,
    BCA_ST_STARTED,
    BCA_ST_REPORTING,
    BCA_ST_REPORTED,
    BCA_ST_REGISTER_FAIL
} bca_state_e;

typedef struct _baidu_ca_s {
    // Start state
    volatile bca_u8_t       state;

    // register message id
#if defined(BCA_LWM2M_REGISTER)
    bca_u16_t               msg_id_reg;
    bca_u16_t               msg_id_unreg;
#endif

    // Configuation
    bca_conf_handler        conf;

    // mutex
    bca_mutex_t             mutex;

    // Configuration
    const char*             uuid;
    const char*             token;
    char*                   report_query;
    bca_size_t              report_query_len;

    const char*             cert;
    bca_size_t              cert_length;

    // Token generator
    bca_u16_t               gen_token;

    // Coap handler
#if defined(BCA_LWM2M_REGISTER)
    bca_coap_handler        reg;
#endif
    bca_coap_handler        sr;
#if defined(BCA_UDP_REPORTER)
    bca_coap_handler        rpt;
#endif

    // Resource list
    bca_res_t*              res_list;
    bca_u32_t               res_size;
    bca_u32_t               res_capa;

    // Response callback
    bca_notify_f            f_response;
    bca_context             ctx_response;

    // Socket context
    bca_context             ctx_socket;

    // Remote address
#if defined(BCA_LWM2M_REGISTER)
    bca_addr_t              addr_reg;
#endif
    bca_addr_t              addr_sr;
#if defined(BCA_UDP_REPORTER)
    bca_addr_t              addr_rpt;
#endif
    bca_u32_t               start_connect_time;
} baidu_ca_t;

static volatile bca_u32_t s_max_connect_time_ms = 0;
static volatile bca_u32_t s_count_connect = 0;
static volatile bca_u32_t s_total_connect_time_ms = 0;

void baidu_ca_get_statistic(bca_u32_t* connect_avg_time,
                            bca_u32_t* max_connect_time,
                            bca_u32_t* count_connect) {
    *max_connect_time = s_max_connect_time_ms / 1000; // unit is ms
    *count_connect = s_count_connect;

    if (s_count_connect > 0) {
        *connect_avg_time =
                (s_total_connect_time_ms * 0.001) / s_count_connect; // unit is ms
    } else {
        *connect_avg_time = 0;
    }
}

BCA_LOC_IMPL bca_status_t bca_engine_coap_handler(bca_context ctx,
                                                  bca_coap_handler coap,
                                                  const bca_msg_t* msg,
                                                  const bca_addr_t* addr) {
    bca_status_t rs = BCA_ERR_INTERNAL;
    baidu_ca_t* ptr = (baidu_ca_t*)ctx;

    if (ptr && msg && addr) {
        rs = BCA_NO_ERR;
        DUER_LOGV("==> bca_engine_coap_handler: state = %d", ptr->state);
#if defined(BCA_LWM2M_REGISTER)

        if (ptr->reg == coap) {
            if (ptr->state == BCA_ST_REGISTER_CONNECTED && ptr->msg_id_reg == msg->msg_id) {
                ptr->msg_id_reg = 0;

                if (msg->msg_code == BCA_MSG_RSP_CREATED) {
                    ptr->state = BCA_ST_REGISTERED;
                }
            } else if (ptr->state == BCA_ST_STOPED && ptr->msg_id_unreg == msg->msg_id) {
                ptr->msg_id_unreg = 0;
            }
        } else
#endif
            if (ptr->sr == coap) {
                if (ptr->state == BCA_ST_SR_DATA_RECEIVING) {
                    if (msg->msg_code == BCA_MSG_RSP_CONTENT) {
                        ptr->state = BCA_ST_SR_INITIALIZED;
                    } else {
                        ptr->state = BCA_ST_REGISTER_FAIL;
                        DUER_LOGE("register fail, code: %d", msg->msg_code);
                    }
                }
#if defined(BCA_UDP_REPORTER)
            } else if (ptr->rpt == coap) {
#else
            } else if (ptr->sr == coap) {
#endif
                if (ptr->f_response) {
                    ptr->f_response(ptr->ctx_response, (bca_msg_t*)msg, NULL);
                }
            }

        DUER_LOGV("<== bca_engine_coap_handler: rs = %d", rs);
    }

    return rs;
}
//unused function
/*
BCA_LOC_IMPL bca_bool baidu_ca_match_address(const bca_addr_t* l,
        const bca_addr_t* r) {
    if (l) {
        DUER_LOGV("baidu_ca_match_address: l = {ip:%s, port:%d, type:%d, length:%d}",
                 l->host, l->port, l->type, l->host_size);
    }

    if (r) {
        DUER_LOGV("baidu_ca_match_address: r = {ip:%s, port:%d, type:%d, length:%d}",
                 r->host, r->port, r->type, r->host_size);
    }

    if (l == r || (l && r && l->port == r->port && l->type == r->type
                   && l->host_size == r->host_size
                   && BCA_MEMCMP(r->host, l->host, r->host_size) == 0)) {
        return BCA_TRUE;
    }

    return BCA_FALSE;
}
*/

#if defined(BCA_LWM2M_REGISTER)
BCA_LOC_IMPL bca_status_t baidu_ca_register(baidu_ca_t* ctx) {
    bca_status_t rs = BCA_ERR_INTERNAL;
    bca_coap_ep_t ep;
    DUER_LOGV("==> baidu_ca_register");

    if (!ctx) {
        goto exit;
    }

    if (!ctx->reg
            && !(ctx->reg = bca_coap_acquire(bca_engine_coap_handler, ctx,
                            ctx->ctx_socket))) {
        goto exit;
    }

    if (ctx->state == BCA_ST_UNREGISTERED) {
        rs = bca_coap_connect(ctx->reg, &ctx->addr_reg, ctx->cert, ctx->cert_length);

        if (rs == BCA_NO_ERR) {
            ctx->state++;
        }
    }

    if (ctx->state != BCA_ST_REGISTER_CONNECTED) {
        goto exit;
    }

    // Send register message
    if (ctx->msg_id_reg == 0) {
        BCA_MEMSET(&ep, 0, sizeof(ep));
        ep.name_ptr = (char*)ctx->uuid;
        ep.name_len = BCA_STRLEN(ctx->uuid);
        ep.type_ptr = (char*)ctx->token;
        ep.type_len = BCA_STRLEN(ctx->token);
        ep.lifetime_ptr = BCA_COAP_LIFETIME;
        ep.lifetime_len = BCA_STRLEN(BCA_COAP_LIFETIME);
        rs = bca_coap_register(ctx->reg, &ep);

        if (rs == 0) {
            rs = BCA_ERR_INTERNAL;
        }

        if (rs > 0) {
            ctx->msg_id_reg = (bca_u16_t)rs;
        } else {
            goto exit;
        }
    }

    rs = bca_coap_data_available(ctx->reg);
exit:
    DUER_LOGV("<== baidu_ca_register: rs = %d", rs);
    return rs;
}
#endif

BCA_LOC_IMPL char* baidu_ca_get_service_router_payload(const char* uuid, const char* token) {
    char* rs = NULL;
    baidu_json* item = baidu_json_CreateObject();

    if (item) {
        baidu_json_AddStringToObject(item, SR_TAG_UUID, uuid);
        baidu_json_AddStringToObject(item, SR_TAG_TOKEN, token);
        rs = baidu_json_PrintUnformatted(item);
        baidu_json_Delete(item);
    }

    return rs;
}

BCA_LOC_IMPL bca_status_t baidu_ca_create_service_router(baidu_ca_t* ctx) {
    bca_status_t rs = BCA_ERR_INTERNAL;
    bca_msg_t msg_reg;
    DUER_LOGV("==> baidu_ca_create_service_router");

    if (!ctx) {
        goto exit;
    }

    if (!ctx->sr) {
        ctx->sr = bca_coap_acquire(bca_engine_coap_handler, ctx, ctx->ctx_socket);
        if (!ctx->sr) {
            goto exit;
        }
    }

    if (ctx->state == BCA_ST_SR_CONNECTING) {
        //record the start connect time
        if (ctx->start_connect_time == 0) {
            ctx->start_connect_time = us_ticker_read();
        }

        rs = bca_coap_connect(ctx->sr, &ctx->addr_sr, ctx->cert, ctx->cert_length);

        if (rs == BCA_NO_ERR) {
            ctx->state++;
            //after success connect, calculate the connect time
            int time_end = us_ticker_read();
            int time_cost = time_end - ctx->start_connect_time;

            if (time_cost > s_max_connect_time_ms) {
                s_max_connect_time_ms = time_cost;
            }

            ++s_count_connect;
            s_total_connect_time_ms += time_cost;
            ctx->start_connect_time = 0;
        }
    }

    if (ctx->state == BCA_ST_SR_DATA_SENDING) {
        BCA_MEMSET(&msg_reg, 0, sizeof(msg_reg));
        msg_reg.msg_type = BCA_MSG_TYPE_CONFIRMABLE;
        msg_reg.msg_code = BCA_MSG_REQ_POST;
        msg_reg.path = BCA_SR_REG_PATH;
        msg_reg.path_len = BCA_STRLEN(BCA_SR_REG_PATH);
        msg_reg.payload = (bca_u8_t*)baidu_ca_get_service_router_payload(ctx->uuid, ctx->token);
        msg_reg.payload_len = BCA_STRLEN((char*)msg_reg.payload);
        rs = bca_coap_send(ctx->sr, &msg_reg);
        baidu_json_release(msg_reg.payload);

        if (rs < BCA_NO_ERR) {
            goto exit;
        }
        ctx->state++;
    }

    if (ctx->state == BCA_ST_SR_DATA_RECEIVING) {
        rs = bca_coap_data_available(ctx->sr);
        if (rs < BCA_NO_ERR) {
            goto exit;
        }
    }

    if (ctx->state == BCA_ST_SR_INITIALIZED) {
        baidu_ca_add_resources(ctx, ctx->res_list, ctx->res_size);
        if (ctx->res_list) {
            BCA_FREE(ctx->res_list);
            ctx->res_list = NULL;
            ctx->res_size = 0;
            ctx->res_capa = 0;
        }
    }

    if (ctx->state == BCA_ST_REGISTER_FAIL) {
        rs = BCA_ERR_REG_FAIL;
    }

exit:
    DUER_LOGV("<== baidu_ca_create_service_router: rs = %d", rs);
    return rs;
}

#if defined(BCA_UDP_REPORTER)
BCA_LOC_IMPL bca_status_t baidu_ca_create_reporter(baidu_ca_t* ctx) {
    bca_status_t rs = BCA_ERR_INTERNAL;
    DUER_LOGV("==> baidu_ca_create_reporter");

    if (!ctx) {
        goto exit;
    }

    if (!ctx->rpt) {
        ctx->rpt = bca_coap_acquire(bca_engine_coap_handler, ctx, ctx->ctx_socket);
        if (!ctx->rpt) {
            goto exit;
        }
    }

    if (ctx->state == BCA_ST_SR_INITIALIZED) {
        rs = bca_coap_connect(ctx->rpt, &ctx->addr_rpt, ctx->cert, ctx->cert_length);

        if (rs == BCA_NO_ERR) {
            ctx->state++;
        }
    }

    if (ctx->state == BCA_ST_RPT_CONNECTED) {
#if defined(BCA_READ_TIMEOUT) && (BCA_READ_TIMEOUT > 0)
        bca_coap_set_read_timeout(ctx->rpt, BCA_READ_TIMEOUT);
#endif
        ctx->state++;
        rs = BCA_NO_ERR;
    }

exit:
    DUER_LOGV("<== baidu_ca_create_reporter: rs = %d", rs);
    return rs;
}
#endif

BCA_LOC_IMPL bca_status_t baidu_ca_run(baidu_ca_t* ctx) {
    bca_status_t rs = BCA_ERR_INTERNAL;

    if (!ctx) {
        goto exit;
    }

    DUER_LOGV("==> baidu_ca_run: ctx = %p, state = %d", ctx, ctx->state);

    switch (ctx->state) {
    case BCA_ST_UNREGISTERED:
    case BCA_ST_REGISTER_CONNECTED:
#if defined(BCA_LWM2M_REGISTER)
        rs = baidu_ca_register(ctx);
        break;
#endif

    case BCA_ST_REGISTERED:
        ctx->state++;
        rs = BCA_NO_ERR;
        break;

    case BCA_ST_SR_CONNECTING:
    case BCA_ST_SR_DATA_SENDING:
    case BCA_ST_SR_DATA_RECEIVING:
        rs = baidu_ca_create_service_router(ctx);
        break;

    case BCA_ST_SR_INITIALIZED:
#if defined(BCA_UDP_REPORTER)
        rs = baidu_ca_create_reporter(ctx);
        break;
#endif

    case BCA_ST_RPT_INITIALIZED:
        ctx->state = BCA_ST_STARTED;
        rs = BCA_NO_ERR;
        break;
    case BCA_ST_STOPED:
    case BCA_ST_STARTED:
        rs = BCA_NO_ERR;
        break;

    default:
        // do noting
        break;
    }

    DUER_LOGV("<== baidu_ca_run: rs = %d", rs);
exit:
    return rs;
}

BCA_EXT_IMPL bca_handler baidu_ca_acquire(const void* data,
                                          bca_size_t size,
                                          bca_context soc_ctx) {
    baidu_ca_t* hdlr = NULL;
    char* addr;
    bca_size_t addr_len;
    DUER_LOGV("==> baidu_ca_acquire");
    BCA_MEMDBG_USAGE();
    hdlr = BCA_MALLOC(sizeof(baidu_ca_t));

    if (!hdlr) {
        DUER_LOGE("    alloc failed...");
        goto error;
    }

    bca_mbed_trace_enable();
    BCA_MEMSET(hdlr, 0, sizeof(baidu_ca_t));
    hdlr->conf = bca_conf_create(data, size);

    if (!hdlr->conf) {
        DUER_LOGE("    create the configuation failed...");
        goto error;
    }

    hdlr->uuid = bca_conf_get_string(hdlr->conf, BCA_CONF_KEY_UUID);

    if (!hdlr->uuid) {
        DUER_LOGE("    obtain the uuid failed...");
        goto error;
    }

    hdlr->token = bca_conf_get_string(hdlr->conf, BCA_CONF_KEY_TOKEN);

    if (!hdlr->token) {
        DUER_LOGE("    obtain the token failed...");
        goto error;
    }

    hdlr->cert = bca_conf_get_string(hdlr->conf, BCA_CONF_KEY_CERT);

    if (!hdlr->cert) {
        DUER_LOGE("    obtain the cert failed...");
        goto error;
    }

    hdlr->cert_length = BCA_STRLEN(hdlr->cert) + 1;
    addr = (char*)bca_conf_get_string(hdlr->conf, BCA_CONF_KEY_HOST);

    if (!addr) {
        DUER_LOGE("    obtain the hostname failed...");
        goto error;
    }

    addr_len = BCA_STRLEN(addr);
    hdlr->report_query_len = BCA_STRLEN(BCA_RPT_QUERY_UUID)
                             + BCA_STRLEN(hdlr->uuid)
                             + 1  // '&'
                             + BCA_STRLEN(BCA_RPT_QUERY_TOKEN)
                             + BCA_STRLEN(hdlr->token);
    hdlr->report_query = BCA_MALLOC(hdlr->report_query_len + 1);

    if (!hdlr->report_query) {
        goto error;
    }

    BCA_SNPRINTF(
            hdlr->report_query,
            hdlr->report_query_len + 1,
            BCA_RPT_QUERY_UUID"%s&"BCA_RPT_QUERY_TOKEN"%s",
            hdlr->uuid,
            hdlr->token);
#if defined(BCA_LWM2M_REGISTER)
    hdlr->addr_reg.host = addr;
    hdlr->addr_reg.host_size = addr_len;
    hdlr->addr_reg.port = bca_conf_get_ushort(hdlr->conf, BCA_CONF_KEY_PORT_REG);
    hdlr->addr_reg.type = BCA_PROTO_UDP;
#endif
#if defined(BCA_UDP_REPORTER)
    hdlr->addr_sr.host = SR_HOST;
    hdlr->addr_sr.host_size = BCA_STRLEN(SR_HOST);
    hdlr->addr_sr.port = SR_PORT;
#else
    hdlr->addr_sr.host = addr;
    hdlr->addr_sr.host_size = addr_len;
    hdlr->addr_sr.port = bca_conf_get_ushort(hdlr->conf, BCA_CONF_KEY_PORT_RPT);
#endif
    hdlr->addr_sr.type = BCA_PROTO_TCP;
#if defined(BCA_UDP_REPORTER)
    hdlr->addr_rpt.host = addr;
    hdlr->addr_rpt.host_size = addr_len;
    hdlr->addr_rpt.port = bca_conf_get_ushort(hdlr->conf, BCA_CONF_KEY_PORT_RPT);
    hdlr->addr_rpt.type = BCA_PROTO_UDP;
#endif
    hdlr->state = BCA_ST_STOPED;
    hdlr->mutex = bca_mutex_create();
    hdlr->gen_token = 0xC000;
    hdlr->ctx_socket = soc_ctx;
exit:
    DUER_LOGV("<== baidu_ca_acquire: hdlr = %p", hdlr);
    return hdlr;
error:
    baidu_ca_release(hdlr);
    hdlr = NULL;
    goto exit;
}

BCA_EXT bca_status_t baidu_ca_add_resources(bca_handler hdlr,
        const bca_res_t list_res[], bca_size_t list_res_size) {
    baidu_ca_t* ctx = (baidu_ca_t*)hdlr;
    bca_status_t rs = BCA_ERR_INTERNAL;

    if (ctx->state >= BCA_ST_SR_INITIALIZED) {
        bca_u8_t i = 0;

        while (i < list_res_size) {
            bca_coap_resource_add(ctx->sr, list_res + i);
            i++;
        }

        rs = BCA_NO_ERR;
        goto exit;
    }

    if (ctx->res_list == NULL) {
        ctx->res_capa = (list_res_size / BCA_RES_INC_COUNT + 1) * BCA_RES_INC_COUNT;
        ctx->res_list = BCA_MALLOC(sizeof(bca_res_t) * ctx->res_capa);
        ctx->res_size = 0;
    } else if (ctx->res_capa < ctx->res_size + list_res_size) {
        ctx->res_capa =
                ((ctx->res_size + list_res_size) / BCA_RES_INC_COUNT + 1) * BCA_RES_INC_COUNT;
        ctx->res_list = (bca_res_t*)BCA_REALLOC(ctx->res_list,
                                                sizeof(bca_res_t) * ctx->res_capa);
    }

    if (ctx->res_list == NULL) {
        DUER_LOGE("    stored the resource failed...");
        goto exit;
    }

    BCA_MEMCPY(ctx->res_list + ctx->res_size, list_res,
               sizeof(bca_res_t) * list_res_size);
    ctx->res_size += list_res_size;
    rs = BCA_NO_ERR;
exit:
    return rs;
}

BCA_EXT_IMPL bca_status_t baidu_ca_do_start(bca_handler hdlr) {
    baidu_ca_t* ctx = (baidu_ca_t*)hdlr;
    bca_status_t rs = BCA_ERR_INTERNAL;
    DUER_LOGV("==> baidu_ca_do_start");

    if (!ctx) {
        goto exit;
    }

    bca_mutex_lock(ctx->mutex);
    rs = BCA_NO_ERR;

    while (ctx->state != BCA_ST_STARTED && rs == BCA_NO_ERR) {
        rs = baidu_ca_run(ctx);
    }

    bca_mutex_unlock(ctx->mutex);
exit:
    DUER_LOGV("<== baidu_ca_do_start: rs = %d", rs);
    return rs;
}

BCA_EXT_IMPL bca_status_t baidu_ca_start(bca_handler hdlr) {
    baidu_ca_t* ctx = (baidu_ca_t*)hdlr;
    bca_status_t rs = BCA_ERR_INTERNAL;
    DUER_LOGV("==> baidu_ca_start");

    if (!ctx) {
        goto exit;
    }

    if (ctx->state == BCA_ST_STOPED) {
        ctx->state = BCA_ST_UNREGISTERED;
    }

    if (ctx->state == BCA_ST_REGISTER_FAIL) {
        rs = BCA_ERR_REG_FAIL;
    } else {
        rs = baidu_ca_do_start(hdlr);
    }

exit:
    DUER_LOGV("<== baidu_ca_start: rs = %d", rs);
    return rs;
}

BCA_EXT_IMPL bca_status_t baidu_ca_report_set_response_callback(
        bca_handler hdlr, bca_notify_f f_response, bca_context context) {
    baidu_ca_t* ctx = (baidu_ca_t*)hdlr;

    if (ctx) {
        ctx->f_response = f_response;
        ctx->ctx_response = context;
        return BCA_NO_ERR;
    }

    return BCA_ERR_INTERNAL;
}

BCA_EXT bca_msg_t* baidu_ca_build_report_message(bca_handler hdlr,
        bca_bool confirmable) {
    baidu_ca_t* ctx = (baidu_ca_t*)hdlr;
    bca_msg_t* msg = BCA_MALLOC(sizeof(bca_msg_t));

    if (!ctx || !msg) {
        BCA_FREE(msg);
        msg = NULL;
        goto exit;
    }

    BCA_MEMSET(msg, 0, sizeof(bca_msg_t));
    msg->msg_type =
            confirmable == BCA_FALSE ? BCA_MSG_TYPE_NON_CONFIRMABLE : BCA_MSG_TYPE_CONFIRMABLE;
    msg->msg_code = BCA_MSG_REQ_POST;
    msg->path = BCA_REPORT_PATH;
    msg->path_len = BCA_STRLEN(BCA_REPORT_PATH);
    msg->query = (bca_u8_t*)ctx->report_query;
    msg->query_len = ctx->report_query_len;
    msg->token_len = sizeof(ctx->gen_token);
    msg->token = BCA_MALLOC(msg->token_len);

    if (msg->token) {
        BCA_MEMCPY(msg->token, &ctx->gen_token, msg->token_len);
    }

    ctx->gen_token++;
exit:
    return msg;
}

BCA_EXT bca_msg_t* baidu_ca_build_response_message(bca_handler hdlr,
        const bca_msg_t* msg, bca_u8_t msg_code) {
    baidu_ca_t* ctx = (baidu_ca_t*)hdlr;
    bca_msg_t* rs = BCA_MALLOC(sizeof(bca_msg_t));

    if (!ctx || !rs || !msg) {
        DUER_LOGE("paramter error: ctx = %p, rs = %p, msg = %p", ctx, rs, msg);
        goto error;
    }

    BCA_MEMSET(rs, 0, sizeof(bca_msg_t));

    if (msg->msg_type == BCA_MSG_TYPE_CONFIRMABLE) {
        rs->msg_type = BCA_MSG_TYPE_ACKNOWLEDGEMENT;
        rs->msg_id = msg->msg_id;
    } else {
        rs->msg_type = msg->msg_type;
    }

    rs->msg_code = msg_code;

    if (msg->token_len > 0 && msg->token) {
        rs->token_len = msg->token_len;
        rs->token = BCA_MALLOC(rs->token_len);

        if (!rs->token) {
            DUER_LOGE("memory overflow!!!");
            goto error;
        }

        BCA_MEMCPY(rs->token, msg->token, rs->token_len);
    }

    goto exit;
error:

    if (rs) {
        BCA_FREE(rs);
        rs = NULL;
    }

exit:
    return rs;
}

/*
 * Release the message that generated by baidu_ca_build_XXXX_message.
 *
 * @Param hdlr, in, the handler will be operated
 * @Param msg, in, the message that remote requested
 */
BCA_EXT void baidu_ca_release_message(bca_handler hdlr, bca_msg_t* msg) {
    if (msg) {
        if (msg->token) {
            BCA_FREE(msg->token);
            msg->token = NULL;
        }

        BCA_FREE(msg);
    }
}

BCA_EXT bca_status_t baidu_ca_send_data(bca_handler hdlr, const bca_msg_t* msg,
                                        const bca_addr_t* addr) {
    baidu_ca_t* ctx = (baidu_ca_t*)hdlr;
    bca_status_t rs = BCA_ERR_INTERNAL;
    DUER_LOGV("==> baidu_ca_send_data");
#if defined(BCA_UDP_REPORTER)

    if (ctx->state >= BCA_ST_SR_INITIALIZED
            && baidu_ca_match_address(&(ctx->addr_sr), addr))
#endif
    {
        DUER_LOGV("    ctx->sr matched: %p", addr);
        rs = bca_coap_send(ctx->sr, msg);
        goto exit;
    }

#if defined(BCA_UDP_REPORTER)

    if (ctx->state >= BCA_ST_RPT_INITIALIZED
        && (!addr || baidu_ca_match_address(&(ctx->addr_rpt), addr))) {
        bca_mutex_lock(ctx->mutex);
        DUER_LOGV("    ctx->rpt matched: %p", addr);
        rs = bca_coap_send(ctx->rpt, msg);
        bca_mutex_unlock(ctx->mutex);
        goto exit;
    }


    rs = baidu_ca_do_start(hdlr);

    if (rs < BCA_NO_ERR) {
        DUER_LOGD("    start failed...");
        goto exit;
    }
#endif
exit:
    DUER_LOGV("<== baidu_ca_send_data: rs = %d", rs);
    BCA_MEMDBG_USAGE();
    return rs;
}

BCA_EXT_IMPL bca_status_t baidu_ca_data_available(bca_handler hdlr,
        const bca_addr_t* addr) {
    baidu_ca_t* ctx = (baidu_ca_t*)hdlr;
    bca_status_t rs = BCA_ERR_INTERNAL;
    DUER_LOGV("==> baidu_ca_data_available");

    if (ctx->state >= BCA_ST_SR_INITIALIZED
#if defined(BCA_UDP_REPORTER)
            && baidu_ca_match_address(&(ctx->addr_sr), addr)
#endif
       ) {
        DUER_LOGV("    ctx->sr matched: %p", addr);
        rs = bca_coap_data_available(ctx->sr);
    }

#if defined(BCA_UDP_REPORTER)

    if (ctx->state >= BCA_ST_RPT_INITIALIZED && (!addr
            || baidu_ca_match_address(&(ctx->addr_rpt), addr))) {
        bca_mutex_lock(ctx->mutex);
        DUER_LOGV("    ctx->rpt matched: %p", addr);
        rs = bca_coap_data_available(ctx->rpt);
        bca_mutex_unlock(ctx->mutex);
    }

#endif

    if (ctx->state < BCA_ST_STARTED) {
        rs = baidu_ca_do_start(hdlr);

        if (rs < BCA_NO_ERR && rs != BCA_ERR_TRANS_WOULD_BLOCK) {
            DUER_LOGW("    start failed...rs:%d", rs);
        }
    }

    DUER_LOGV("<== baidu_ca_data_available: rs = %d", rs);
    return rs;
}

BCA_EXT bca_status_t baidu_ca_exec(bca_handler hdlr) {
    baidu_ca_t* ctx = (baidu_ca_t*)hdlr;
    bca_status_t rs = BCA_NO_ERR;
    bca_u32_t timestamp = bca_timestamp() / 1000;
    DUER_LOGV("==> baidu_ca_exec");

    if (ctx) {
#if defined(BCA_LWM2M_REGISTER)
        bca_coap_exec(ctx->reg, timestamp);
#endif
#if defined(BCA_UDP_REPORTER)
        bca_coap_exec(ctx->rpt, timestamp);
#endif
        bca_coap_exec(ctx->sr, timestamp);
    }

    DUER_LOGV("<== baidu_ca_exec: rs = %d", rs);
    return rs;
}

BCA_EXT_IMPL bca_status_t baidu_ca_stop(bca_handler hdlr) {
    baidu_ca_t* ctx = (baidu_ca_t*)hdlr;
    bca_status_t rs = BCA_NO_ERR;
    DUER_LOGV("==> baidu_ca_stop");

    if (ctx->state > BCA_ST_STOPED) {
        ctx->state = BCA_ST_STOPED;

        if (ctx->sr) {
            bca_coap_release(ctx->sr);
            ctx->sr = NULL;
        }

#if defined(BCA_UDP_REPORTER)

        if (ctx->rpt) {
            bca_coap_release(ctx->rpt);
            ctx->rpt = NULL;
        }

#endif
#if defined(BCA_LWM2M_REGISTER)

        if (ctx->reg) {
            bca_coap_unregister(ctx->reg);
            bca_coap_release(ctx->reg);
            ctx->reg = NULL;
        }

#endif
    }

    DUER_LOGV("<== baidu_ca_stop: rs = %d", rs);
    return rs;
}

BCA_EXT_IMPL bca_status_t baidu_ca_release(bca_handler hdlr) {
    baidu_ca_t* ctx = (baidu_ca_t*)hdlr;
    DUER_LOGV("==> baidu_ca_release");

    if (ctx) {
        if (ctx->conf) {
            bca_conf_destroy(ctx->conf);
            ctx->conf = NULL;
        }

        baidu_ca_stop(ctx);

        if (ctx->report_query) {
            BCA_FREE(ctx->report_query);
            ctx->report_query = NULL;
        }

        if (ctx->res_list) {
            BCA_FREE(ctx->res_list);
            ctx->res_list = NULL;
        }

        bca_mbed_trace_disable();
        bca_mutex_destroy(ctx->mutex);
        ctx->mutex = NULL;
        BCA_FREE(ctx);
    }

    BCA_MEMDBG_USAGE();
    DUER_LOGV("<== baidu_ca_release");
    return BCA_NO_ERR;
}
